package Tier2;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

import Shared.*;

public class PartyManagerImpl {

    public static final Serveur _serveur = new Serveur();
    private HashMap<String, PartyManagerImpl.Callback> _callback = new HashMap<String, PartyManagerImpl.Callback>();
    
    private ArrayList<Party> _parties = new ArrayList<Party>();
    private ArrayList<Player> _players = new ArrayList<Player>();
    private int _parties_id = 1;
    
    public ArrayList<Party> getParties() { return _parties; }
    public ArrayList<Player> getPlayers() { return _players; }
    
    /**
     * Un joueur crée une partie de nb_joueurs
     * et est ajouté à cette partie
     * @param player
     * @param nb_joueurs
     */
    public Party createParty(Player player, int nb_joueurs) {
	Party party = new Party(nb_joueurs, _parties_id++);
	party.setManager(this);
	_parties.add(party);
	
	party.addPlayer(player);
	return party;
    }
    
    //TODO top players + position du player
    public Players getClassement() { 
    	Players classement = _serveur.listerPlayers();
    	Collections.sort(classement.liste, new ComparateurPlayerScore());
		return classement;
    }
    
    // TODO - encours - peut etre inutile
    public int getScores(Player player) { return -1; }
    
    /**
     * Récupère un joueur et toutes ses informations stockées dans la bd.
     * @param name
     * @return Player
     */
    public Player getPlayer(String user, String name) { 
	Player player = _serveur.getPlayer(name);
	player.setCallback(user);
	System.out.println(name + "("+user+")" + _callback.get(user).hashCode());
	_players.add(player);
	return player;
    }
    
    /**
     * Connecte un joueur à l'application et l'ajoute à la liste des joueurs connecté "_players"
     * @param player
     * @return true si la connexion est ok sinon false.
     */
    public boolean login(String name, String password) { 
    	try {
    	    if(_serveur.connectPlayer(name, password) && !alreadyLogin(name)){
    		return true;
    	    } else { 
    		return false; 
    	    }
    	} catch (Exception e) {
    	    System.out.println("Erreur checkAuth");
    	    e.printStackTrace();
    	}
    	return false;
    }
    
    /**
     * Verifie que l'utilisateur tentant de s'authentifier n'est pas déjà authentifié
     * @param name
     * @return true si l'identifiant donné fait partie des personnes loggées
     */
    public boolean alreadyLogin(String name) {
    	for(Player pl : _players){
    		if(pl.getName().equals(name)) {
    			return true;
    		}
    	}
    	return false;
    }
    
     /**
     * Rejoins la partie donnée si celle-ci n'est déjà pleine / en attente de joueur
     * @param hashParty (hashCode d'une partie présente dans _parties)
     * @return la partie rejointe si le joueur est ajouté - une partie null avec un id -1 si le joueur est deja dans la partie
     */
    public Party joinParty(int idParty, Player player) {
    	for(Party p : _parties) {
    	    if(p.get_id() == idParty) {
    	    	if(!p.getPlayers().containsKey(player)) {
	    	    	if( p.addPlayer(player)) {
	    	    	    return p;
	    	    	} else { 
	    	    	    System.out.println("Erreur - la partie est en cours. Vous ne pouvez pas la rejoindre."); 
	    	    	    return new Shared.Party(0, -1); 
	    	    	}
    	    	} else { System.out.println("Vous êtes déjà dans la partie. Retournez y"); return p;}
    	    } else { 
    	    	System.out.println("Erreur : id incorrect.");
    	    	return new Shared.Party(0, -1); 
    	    }
    	}
    	return new Shared.Party(0, -1);
    }
    
    /**
     * Déconnecte un joueur - supprime le joueur de la liste des joueurs connectés.
     * @param player
     */
    public void logout(String user, Player player) {
    	_players.remove(player);
    	_callback.remove(user);
    }
    
    /**
     * Appel la fonction de creation de compte du serveur 
     * @param name - login de l'utilisateur
     * @param password
     * @return true si la création du compte est un succès
     */
    public boolean creerCompte(String name, String password) { 
    	if(!name.isEmpty() && !password.isEmpty()) {
    	    return _serveur.createPlayer(name, password);
    	} else { 
    	    return false; 
    	}
    }
    
    /**
     * Le joueur abandonne la partie
     * @param party
     * @param player
     */
    public void quitterParty(Party party, Player player) {
        for(Party p : _parties) {
            if(p==party) {
        	p.quitterParty(player);
            }
        }
    }
    
  //+----------------------------------------------------------------------+
  //| 
  //| Proxies 
  //| 
  //+----------------------------------------------------------------------+
    /**
     * Le callback permet au serveur de notifier au client en mode push
     * @param user ~ hashCode de l'utilisateur
     * @param callbackRMI ~ callback à utiliser pour notifier l'utilisateur
     * @param callbackIIOP ~ ou
     */
    public void addCallback(String user, Tier2.RMI_PartyManager.Callback callbackRMI,
	    Tier2.ORB_PartyManager.Callback callbackIIOP) {
	PartyManagerImpl.Callback callback = this.new Callback(callbackRMI, callbackIIOP);
	_callback.put(user, callback);
    }
    public void callback(String user, String msg) {
	try {
	    _callback.get(user).callback(msg);
	} catch (RemoteException e) {
	    System.out.println("Could not callback");
	    e.printStackTrace();
	}
    }
    
    public class Callback {
	private Tier2.ORB_PartyManager.Callback _callbackIIOP = null;
	private Tier2.RMI_PartyManager.Callback _callbackRMI = null;
	
	public Callback(Tier2.RMI_PartyManager.Callback callbackRMI, 
		Tier2.ORB_PartyManager.Callback callbackIIOP) {
		_callbackRMI = callbackRMI;
		_callbackIIOP = callbackIIOP;
	}
	public void callback(String msg) throws RemoteException {
	    if(_callbackRMI!=null) {
		_callbackRMI.callback(msg);
	    } else {
		_callbackIIOP.callback(msg);
	    }
	}
    }
    
    /**
     * Proxy ORB de PartyManagerImpl. Doit spécifier toutes les méthodes de PartyManagerImpl.
     * Exemple : 
     * <pre>
     * function Type method_name() { 
     *     return parent.method_name()
     * }
     * </pre>
     */
    public class IIOP extends Tier2.ORB_PartyManager.PartyManagerPOA {
	
	private PartyManagerImpl parent;
	public IIOP() { parent = new PartyManagerImpl(); }
	
	@Override
	public void callback(String user, Tier2.ORB_PartyManager.Callback callback) {
	    parent.addCallback(user, null, callback);
	} 
	
	@Override
	public boolean login(String name, String password) {
		return parent.login(name, password);
	}
	@Override
	public void logout(String user, String player) {
		try {
		    parent.logout(user, Player.deserialize(player));
		} catch (Exception e) {
		    System.out.println("Logout failed");
		    e.printStackTrace();
		}
	}
	@Override
	public boolean creerCompte(String name, String password) {
		return parent.creerCompte(name, password);
	}
	@Override
	public String createParty(String player, int nb_joueurs) {
		try {
		    return Party.serialize(parent.createParty(Player.deserialize(player), nb_joueurs));
		} catch (Exception e) {
		    System.out.println("CreateParty failed");
		    e.printStackTrace();
		}
		return null;
	}
	@Override
	public String getParties() {
		try {
		    return Parties.serialize(new Parties(parent.getParties()));
		} catch (Exception e) {
		    System.out.println("GetParties failed");
		    e.printStackTrace();
		}
		return null;
	}
	@Override
	public String getClassement() {
		try {
		    return Players.serialize(parent.getClassement());
		} catch (Exception e) {
		    System.out.println("GetClassement failed");
		    e.printStackTrace();
		}
		return null;
	}
	@Override
	public int getScores(String player) {
		try {
		    return parent.getScores(Player.deserialize(player));
		} catch (Exception e) {
		    System.out.println("Getscores failed");
		    e.printStackTrace();
		}
		return -1;
	}  
	@Override
	public String getPlayer(String user, String name) {
		try {
		    return Player.serialize(parent.getPlayer(user, name));
		} catch (Exception e) {
		    System.out.println("Getplayer failed");
		    e.printStackTrace();
		}
		return null;
	}
	@Override
	public String joinParty(int hashParty, String player) {
		try {
		    return Party.serialize(parent.joinParty(hashParty, Player.deserialize(player)));
		} catch (Exception e) {
		    System.out.println("Joinparty failed");
		    e.printStackTrace();
		}
		return null;
	}
	@Override
	public void quitterParty(String party, String player) {
		try {
		    parent.quitterParty(Party.deserialize(party), Player.deserialize(player));
		} catch (Exception e) {
		    System.out.println("quitterParty failed");
		    e.printStackTrace();
		}
	}
    }
    
    /**
     * Proxy RMI de PartyManagerImpl. Doit spécifier toutes les méthodes de PartyManagerImpl.
     * Exemple : 
     * <pre>
     * function Type method_name() { 
     *     return parent.method_name()
     * }
     * </pre>
     */
    public class RMI implements Tier2.RMI_PartyManager.PartyManager {
	private PartyManagerImpl parent;
	public RMI() { parent = new PartyManagerImpl(); }

	@Override
	public void callback(String user, Tier2.RMI_PartyManager.Callback callback) throws RemoteException {
	    parent.addCallback(user, callback, null);
	}
	
	@Override
	public boolean login(String name, String password) {
		return parent.login(name, password);
	}
	@Override
	public void logout(String user, Player player) {
		parent.logout(user, player);
	}
	@Override
	public boolean creerCompte(String name, String password) {
		return parent.creerCompte(name, password);
	}
	@Override
	public Party createParty(Player player, int nb_joueurs) {
		return parent.createParty(player, nb_joueurs);
	}
	@Override
	public ArrayList<Party> getParties() {
		return parent.getParties();
	}
	@Override
	public Players getClassement() {
		return parent.getClassement();
	}
	@Override
	public int getScores(Player player) {
		return parent.getScores(player);
	}  
	@Override
	public Player getPlayer(String user, String name) {
		return parent.getPlayer(user, name);
	}
	@Override
	public Party joinParty(int hashParty, Player player) {
		return parent.joinParty(hashParty, player);
	}

	@Override
	public void quitterParty(Party party, Player p) throws RemoteException {
	    parent.quitterParty(party, p);
	}
    }
}